﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Core
{
    /// <summary>
    /// Class for network interactions with the Awesom-O core
    /// </summary>
    /// <auther>Thomas (ejact)</auther>
    public abstract class AoSocket : IDisposable
    {
        private string ip = "127.0.0.1";
        private int port = 2004; 
        private Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private Thread receiverthread;

        public AoSocket() {}

        public AoSocket(string ip)
        {
            this.ip = ip;
        }

        public AoSocket(string ip, int port)
        {
            this.ip = ip;
            this.port = port;
        }

        public bool IsClientConnected
        {
            get
            {
                try { return client.Connected; }
                catch (Exception e)
                {
                    SocketErrorHandler(e.Message);
                    return false;
                }
            }
        }

        protected bool Connect()
        {
            try
            {
                client.Connect(new IPEndPoint(IPAddress.Parse(ip),port));
                return client.Connected;
            }
            catch (Exception e) { SocketErrorHandler(e.Message);  }
            return false;
        }

        protected bool StartListener()
        {
            try
            {
                if (client.Connected)
                {
                    receiverthread = new Thread(new ParameterizedThreadStart(this.Listener_Worker));
                    receiverthread.Start();
                    return true;
                }
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return false;
        }

        protected bool Disconnect()
        {
            try
            {
                client.Disconnect(true);
                return client.Connected;
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return false;
        }

        protected int Send(byte[] buffer, int length)
        {
            try
            {
                if (client.Connected)
                    return client.Send(buffer, length, SocketFlags.None);
            }
            catch (Exception e) { SocketErrorHandler(e.Message); }
            return 0;
        }

        protected void Listener_Worker(object obj)
        {
            try
            {
                while (client.Connected)
                {
                    using (NetworkStream stream = new NetworkStream(client))
                    {
                        if (stream.DataAvailable)
                        {
                            using (BinaryReader r = new BinaryReader(stream))
                            {
                                int length = r.ReadInt32();
                                if (length == 0) { break; }
                                byte[] buffer = new byte[length];
                                buffer = r.ReadBytes(length);
                                PacketHandler(buffer);
                            }
                        }
                    }
                    Thread.Sleep(100);
                }
                SocketErrorHandler("Core disconnected");
            }
            catch (ThreadAbortException) { }
            catch (Exception e) { SocketErrorHandler(e.Message); }
        }

        abstract protected void PacketHandler(byte[] packets);

        abstract protected void SocketErrorHandler(string errmsg);

        public void Dispose()
        {
            try 
            { 
                client.Close();
                if (receiverthread != null)
                {
                    if (receiverthread.IsAlive)
                    {
                        receiverthread.Abort();
                        receiverthread.Join();
                    }
                }
            }
            catch { }
        }
    }
}
